/****************************************************************\

Copyright 2004 Enzo Michelangeli

This file is part of the KadC library.

KadC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

KadC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with KadC; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

In addition, closed-source licenses for this software may be granted
by the copyright owner on commercial basis, with conditions negotiated
case by case. Interested parties may contact Enzo Michelangeli at one
of the following e-mail addresses (replace "(at)" with "@"):

 em(at)em.no-ip.com
 em(at)i-t-vision.com

\****************************************************************/

#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <sys/time.h>
#include <arpa/inet.h>

#include <KadClog.h>
#include <OPCodes.h>


/*Set these to 1 to log these 0 to turn them off*/
#define ERROR_PRINT_ONOFF 1
#define GENERAL_PRINT_ONOFF 0
#define VERBOSE_PRINT_ONOFF 0
#define FINDNODE_PRINT_ONOFF 0
#define SPECIAL_PRINT_ONOFF 0
#define STARTUP_PRINT_ONOFF 1
#define MSGTYPE_PRINT_ONOFF 0
#define RTABLE_PRINT_ONOFF 1
#define SEARCH_PRINT_ONOFF 1
#define BOT_PRINT_ONOFF 1

static FILE **logf = NULL;
static pthread_mutex_t console_io_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct timeval start_real_time;
static struct timeval start_time;
static struct timeval cur_time;//Needed for DVN time
static unsigned int did_reset_time = 0;
static dvn_gettime g_time_func = NULL;

void KadClog_set_start_time(void) {

  gettimeofday(&start_time, NULL);

}

void KadClog_set_start_dvn_time(struct timeval dvn_time) {

  // This will be called as the node starts,
  // so we should check and not reset the time
  if(did_reset_time == 0)
    {
      memcpy(&start_time, &dvn_time, sizeof(struct timeval));
      gettimeofday(&start_real_time, NULL);
      did_reset_time = 1;
    }
}

void KadClog_set_current_dvn_time(struct timeval dvn_time) {

  if(cur_time.tv_sec < dvn_time.tv_sec)
    {
      memcpy(&cur_time, &dvn_time, sizeof(struct timeval));
    }
}

time_t KadClog_get_current_dvn_time() {
  if (g_time_func) {
    return (*g_time_func)();
  }
  return time(NULL);
}

dvn_gettime KadClog_set_time_func(dvn_gettime time_func) {
  dvn_gettime old_time_func = g_time_func;
  g_time_func = time_func;
  return old_time_func;
}

FILE *KadClog_open(char *filename) {
	static FILE *f;
	f = fopen(filename, "ab");
	KadClog_setfile(&f);
	return f;
}

void KadClog_setfile(FILE **f) {
	logf = f;
}

void KadC_flog(int level, FILE *f, const char *fmt, ...) {
	va_list ap;
	va_start(ap, fmt);

	pthread_mutex_lock(&console_io_mutex);
	vfprintf(f, fmt, ap);
	if(logf == NULL || *logf == stdout)
		fflush(stdout);
	pthread_mutex_unlock(&console_io_mutex);
	va_end(ap);
}


void KadC_log(int level, const char *fmt, ...) {
	switch ( level	)
	{
	case ERROR_PRINT:
	  if(ERROR_PRINT_ONOFF == OFF) return;
	  break;
	case GENERAL_PRINT:
	  if(GENERAL_PRINT_ONOFF == OFF) return;
	  break;
	case VERBOSE_PRINT:
	  if(VERBOSE_PRINT_ONOFF == OFF) return;
	  break;
	case FINDNODE_PRINT:	
	  if(FINDNODE_PRINT_ONOFF == OFF) return;	
	  break;	
	case SPECIAL_PRINT:
	  if(SPECIAL_PRINT_ONOFF == OFF) return;
	  break;
	case STARTUP_PRINT:
	  if(STARTUP_PRINT_ONOFF == OFF) return;
	  break;
	case MSGTYPE_PRINT:
	  if(MSGTYPE_PRINT_ONOFF == OFF) return;
	  break;
	case RTABLE_PRINT:
	  if(RTABLE_PRINT_ONOFF == OFF) return;
	  break;
	case SEARCH_PRINT:
	  if(SEARCH_PRINT_ONOFF == OFF) return;
	  break;
	case BOT_PRINT:
	  if(BOT_PRINT_ONOFF == OFF) return;
	  break;
	}
	va_list ap;
	va_start(ap, fmt);
	//send special print to a file, rest to stdout
	pthread_mutex_lock(&console_io_mutex);
	if(level == SPECIAL_PRINT && logf != NULL) {
		vfprintf(*logf, fmt, ap);
		fflush(*logf);
	} else {
		vfprintf(stdout, fmt, ap);
		fflush(stdout);
	}
	pthread_mutex_unlock(&console_io_mutex);
	va_end(ap);
}

void KadC_logt_parsed(unsigned char* data, unsigned long int src_ip){

  unsigned char opcode = data[1];
  struct sockaddr_in n_addr;
  char* a_addr = NULL;
  char temp_addr[10] = "n/a";

	n_addr.sin_addr.s_addr = htonl(src_ip);
	a_addr = inet_ntoa(n_addr.sin_addr);
	if(a_addr == NULL)
	  {
	    a_addr = temp_addr;
	  }

	switch (opcode) {
		case KADEMLIA_BOOTSTRAP_REQ:
		        KadC_logt(MSGTYPE_PRINT,"ip: %s - Sending packet of type: Bootstrap Request\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_BOOTSTRAP_RES:
		        KadC_logt(MSGTYPE_PRINT,"ip: %s - Sending packet of type: Bootstrap Response\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_HELLO_REQ:
		        KadC_logt(MSGTYPE_PRINT,"ip: %s - Sending packet of type: Hello Request\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_HELLO_RES:
		        KadC_logt(MSGTYPE_PRINT,"ip: %s - Sending packet of type: Hello Response\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_REQ:
		        KadC_logt(MSGTYPE_PRINT,"ip: %s - Sending packet of type: Kademlia Request\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_RES:
		        KadC_logt(MSGTYPE_PRINT,"ip: %s - Sending packet of type: Kademlia Response\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_SEARCH_REQ:
			break;
		case KADEMLIA_SEARCH_RES:
			break;
		case KADEMLIA_PUBLISH_REQ:
		        KadC_logt(GENERAL_PRINT,"ip: %s - Sending packet of type: Publish Request\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_PUBLISH_RES:
			break;
		case KADEMLIA_SRC_NOTES_REQ:
		        KadC_logt(GENERAL_PRINT,"ip: %s - Sending packet of type: Search Notes Request\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_SRC_NOTES_RES:
		        KadC_logt(GENERAL_PRINT,"ip: %s - Sending packet of type: Search Notes Response\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_PUB_NOTES_REQ:
			break;
		case KADEMLIA_PUB_NOTES_RES:
			break;
		case KADEMLIA_FIREWALLED_REQ:
		        KadC_logt(MSGTYPE_PRINT,"ip: %s - Sending packet of type: Firewall Request\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_FIREWALLED_RES:
		        KadC_logt(MSGTYPE_PRINT,"ip: %s - Sending packet of type: Firewall Response\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_FIREWALLED_ACK:
		        KadC_logt(MSGTYPE_PRINT,"ip: %s - Sending packet of type: Firewall Ack\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_FINDBUDDY_REQ:
		        KadC_logt(MSGTYPE_PRINT,"ip: %s - Sending packet of type: Find Buddy Request\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_FINDBUDDY_RES:
		        KadC_logt(MSGTYPE_PRINT,"ip: %s - Sending packet of type: Find Buddy Response\n", a_addr);//Completes the string started in net.c
			break;
		case KADEMLIA_CALLBACK_REQ:
			break;
		default: 
		        KadC_logt(ERROR_PRINT,"Packet type not regular kademlia, discarding\n");
		
	}

}

void KadC_logt(int level, const char *fmt, ...) {
  //Variable to hold the value of the real time
  struct timeval real_time;

	switch ( level	)
	{
	case ERROR_PRINT:
	  if(ERROR_PRINT_ONOFF == OFF) return;
	  break;
	case GENERAL_PRINT:
	  if(GENERAL_PRINT_ONOFF == OFF) return;
	  break;
	case VERBOSE_PRINT:
	  if(VERBOSE_PRINT_ONOFF == OFF) return;
	  break;
	case FINDNODE_PRINT:	
	  if(FINDNODE_PRINT_ONOFF == OFF) return;	
	  break;	
	case SPECIAL_PRINT:
	  if(SPECIAL_PRINT_ONOFF == OFF) return;
	  break;
	case STARTUP_PRINT:
	  if(STARTUP_PRINT_ONOFF == OFF) return;
	  break;
	case MSGTYPE_PRINT:
	  if(MSGTYPE_PRINT_ONOFF == OFF) return;
	  break;
	case RTABLE_PRINT:
	  if(RTABLE_PRINT_ONOFF == OFF) return;
	  break;
	case SEARCH_PRINT:
	  if(SEARCH_PRINT_ONOFF == OFF) return;
	  break;
	case BOT_PRINT:
	  if(BOT_PRINT_ONOFF == OFF) return;
	  break;
	}

	struct timeval now;
	// Use DVN time instead of system time
	memcpy(&now, &cur_time, sizeof(struct timeval));

	va_list ap;
	va_start(ap, fmt);

	//Populates the real time
	gettimeofday(&real_time, NULL);

	//send special_print to a file rest to stdout
	pthread_mutex_lock(&console_io_mutex);
	if(level == SPECIAL_PRINT && logf != NULL) {
		fprintf(*logf, "[%u.%.5u, %u] ",
			(unsigned int)(now.tv_sec - start_time.tv_sec),
			(unsigned int)(now.tv_usec - start_time.tv_usec), 
			(unsigned int)(real_time.tv_sec - start_real_time.tv_sec));
		vfprintf(*logf, fmt, ap);
		
		fflush(*logf);
		
	}

	else {
		fprintf(stdout, "[%u.%.5u, %u] ",
			(unsigned int)(now.tv_sec - start_time.tv_sec),
			(unsigned int)(now.tv_usec - start_time.tv_usec),
			(unsigned int)(real_time.tv_sec - start_real_time.tv_sec));
		vfprintf(stdout, fmt, ap);
		fflush(stdout);
	}
	/*	
	if(logf == NULL || *logf == stdout) {
		fprintf(stdout, "[%u.%.5u]",(unsigned int)(now.tv_sec - start_time.tv_sec),(unsigned int)(now.tv_usec - start_time.tv_usec));
		vfprintf(stdout, fmt, ap);
		fflush(stdout);
	} else {
		fprintf(*logf, "[%u.%.5u]",(unsigned int)(now.tv_sec - start_time.tv_sec),(unsigned int)(now.tv_usec - start_time.tv_usec));
		vfprintf(*logf, fmt, ap);
	}	
	*/
	pthread_mutex_unlock(&console_io_mutex);
	va_end(ap);
	
}



char *KadC_getsn(char *s, int size) {
	int n;

	n = read(0, s, size-2);
	if(n<=0) {
		s = NULL;
	}
	else
		s[n] = 0;

	return s;
}
